﻿/*
 *  Copyright 2012 HEINER DIRECT GmbH & Co. KG
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 * @Author Rafael Dabrowski
 * 
*/
using System;
using System.Linq;
using System.Data;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Collections.Generic;
using System.Globalization;

namespace HDI.SharePoint.Webparts.HDIAggregatedView.HDIAggregatedViewWP
{
    [ToolboxItemAttribute(false)]
    public class HDIAggregatedViewWP : WebPart, IWebEditable
    {
        public Config Config { get; set; }
        [Personalizable(true)]
        public String sConfig { set; get; }


        protected String _error = "";
        protected String _debug = "";
        public void Debug(string debug, bool error)
        {
            if (error)
            {
                this._error += String.Format("<div class='error'>{0}</div>", debug);
            }
            else
            {
                this._debug += String.Format("<pre>{0}</pre>", HttpUtility.HtmlEncode(debug));
            }
        }
        public void Debug(string debug)
        {
            this.Debug(debug, false);
        }

        EditorPartCollection IWebEditable.CreateEditorParts()
        {
            var editors = new List<EditorPart>();
            var editorPart = new HDIAggregatedViewEditorPart();
            editorPart.ID = ID + "_editorPart";
            editors.Add(editorPart);
            return new EditorPartCollection(editors);
        }

        /// <summary>
        /// Returns parent web part to editor part
        /// </summary>
        object IWebEditable.WebBrowsableObject
        {
            get { return this; }
        }

        SPWeb Web;
        SPList List;

        private void ReferencePrevalidatedScript(string objectToValidate, string scriptPath)
        {
            Controls.Add(new LiteralControl(string.Format(
                @"<script>if (typeof ({0}) == ""undefined"") {{
																document.write(""<script"" + "" src='{1}'><"" + ""/script>"");
																}}
															</script>",
                objectToValidate, scriptPath)));
        }


        /// <summary>
        /// Creates the CAML neccesary to live Filter
        /// </summary>
        /// <returns>CAML Query</returns>
        protected string createCaml()
        {
            try
            {
                string caml = "";
                int count = 0;

                if (Config.Filter.Count > 0)
                {
                    foreach (var filterField in Config.Filter)
                    {
                        if (Page.Request.Params[ID + filterField.internalName] != null)
                        {
                            if (Page.Request.Params[ID + filterField.internalName] != "")
                            {
                                count++;
                                switch (filterField.type)
                                {
                                    case "DateTime":
                                        string dateString = Page.Request.Params[ID + filterField.internalName];
                                        DateTime start = DateTime.Parse((dateString.Split('-'))[0].Trim());
                                        DateTime end = DateTime.Parse((dateString.Split('-'))[1].Trim());

                                        caml += string.Format("<And><Geq><FieldRef Name='{0}' /><Value Type='DateTime'>{1}</Value></Geq><Leq><FieldRef Name='{0}' /><Value Type='DateTime'>{2}</Value></Leq></And>", filterField.internalName, start.ToString("yyyy-MM-dd"), end.ToString("yyyy-MM-dd"));
                                        break;
                                    case "Number":
                                    case "Integer":
                                    case "Currency":
                                        string numberOperation = "Eq";

                                        switch (Page.Request.Params[ID + filterField.internalName + "_option"])
                                        {
                                            case "-2":
                                                numberOperation = "Lt";
                                                break;
                                            case "-1":
                                                numberOperation = "Leq";
                                                break;
                                            case "0":
                                                numberOperation = "Eq";
                                                break;
                                            case "1":
                                                numberOperation = "Geq";
                                                break;
                                            case "2":
                                                numberOperation = "Gt";
                                                break;
                                        }
                                        caml += String.Format("<{3}><FieldRef Name='{0}' /><Value Type='{1}'>{2}</Value></{3}>", filterField.internalName, filterField.type, Page.Request.Params[ID + filterField.internalName], numberOperation);
                                        break;
                                    case "Text":
                                    case "Note":
                                        string textOperation = "Eq";
                                        switch (Page.Request.Params[ID + filterField.internalName + "_option"])
                                        {
                                            case "0":
                                                textOperation = "Eq";
                                                break;
                                            case "1":
                                                textOperation = "BeginsWith";
                                                break;
                                            case "2":
                                                textOperation = "Contains";
                                                break;

                                        }
                                        caml += String.Format("<{3}><FieldRef Name='{0}' /><Value Type='{1}'>{2}</Value></{3}>", filterField.internalName, filterField.type, Page.Request.Params[ID + filterField.internalName], textOperation);
                                        break;
                                    default:
                                        caml += String.Format("<Eq><FieldRef Name='{0}' /><Value Type='{1}'>{2}</Value></Eq>", filterField.internalName, filterField.type, Page.Request.Params[ID + filterField.internalName]);
                                        break;
                                }
                                if (count > 1)
                                    caml = "<And>" + caml + "</And>";
                            }
                        }
                    }
                }
                caml = "<Where>" + caml + "</Where>";
                Debug(caml);
                return caml;
            }
            catch (Exception ex)
            {
                Debug("CAML: " + ex.Message, true);
                return "";
            }
        }

        protected string getFilter()
        {
            try
            {
                string filter = "<h3>Filter</h3>";
                foreach (var filterField in Config.Filter)
                {
                    switch (filterField.type)
                    {
                        case "DateTime":
                            filter += String.Format("<label for='filter_{0}'>{1}</label><br/><input type='text' name='{3}{0}' id='{3}filter_{0}' class='date' value='{2}'/>", filterField.internalName, filterField.name, Page.Request.Params[ID + filterField.internalName] ?? "", ID);
                            break;
                        case "Lookup":

                            if (Config == null)
                            {
                                throw new Exception("Bitte die Configuration ausführen.");
                            }

                            string sWeb = "";
                            if (String.IsNullOrEmpty(Config.Web.name))
                            {
                                sWeb = SPContext.Current.Web.ServerRelativeUrl;
                            }
                            else
                            {
                                sWeb = Config.Web.url.Replace(SPContext.Current.Site.Url, "");
                            }
                            if (!sWeb.StartsWith("/"))
                            {
                                sWeb = "/" + sWeb;  
                            }
                            List<string> lookupValues = new List<string>();
                            lookupValues.Add("Bitte Auswählen... ");
                            SPFieldLookup lookupField = null;
                            using (Web = SPContext.Current.Site.OpenWeb(sWeb))
                            {
                                SPList List = Web.Lists[Config.List.name];
                                lookupField = (SPFieldLookup)List.Fields.GetFieldByInternalName(filterField.internalName);
                                if (Web.ID == lookupField.LookupWebId)
                                {
                                    SPList targetList = Web.Lists[new Guid(lookupField.LookupList)];

                                    foreach (SPListItem item in targetList.GetItems(lookupField.LookupField))
                                    {
                                        lookupValues.Add(item[lookupField.LookupField] as String);
                                    }
                                }
                                else
                                {
                                    using (SPWeb targetSite = SPContext.Current.Site.OpenWeb(lookupField.LookupWebId))
                                    {
                                        // Get the name of the list where this field gets information.
                                        SPList targetList = targetSite.Lists[new Guid(lookupField.LookupList)];

                                        foreach (SPListItem item in targetList.GetItems(lookupField.LookupField))
                                        {
                                            lookupValues.Add(item[lookupField.LookupField] as String);
                                        }
                                    }
                                }
                            }




                            string options = String.Join("</option><option>", lookupValues.ToArray());
                            if ((Page.Request.Params[ID + filterField.internalName] ?? "") != "")
                            {
                                options = options.Replace("option>" + Page.Request.Params[ID + filterField.internalName] ?? "O_o", "option selected>" + Page.Request.Params[ID + filterField.internalName] ?? "");
                            }
                            filter += String.Format("<label for='filter_{0}'>{1}</label><select name='{3}{0}' id='{3}filter_{0}' class='lookup'><option value=''>{2}</option></select><br>", filterField.internalName, filterField.name, options, ID);
                            break;
                        //field.type == "Number" || field.type == "Integer" || field.type == "Currency" ||
                        case "Number":
                        case "Integer":
                        case "Currency":
                            string numberOptions = "<select name='{3}{0}_option'><option value='-2'>Kleiner</option><option value='-1'>Kleiner-Gleich</option><option value='0'>Gleich</option><option value='1'>Größer-Gleich</option><option value='2'>Größer</option></select>";
                            if ((Page.Request.Params[ID + filterField.internalName + "_option"] ?? "") != "")
                            {

                                numberOptions = numberOptions.Replace("'" + Page.Request.Params[ID + filterField.internalName + "_option"] + "'", "'" + Page.Request.Params[ID + filterField.internalName + "_option"] + "' selected");
                            }
                            filter += String.Format("<label for='filter_{0}'>{1}</label><br/><input name='{3}{0}' id='{3}filter_{0}'type='text' class='' value='{2}'/><br/>" + numberOptions, filterField.internalName, filterField.name, Page.Request.Params[ID + filterField.internalName] ?? "", ID);
                            break;
                        case "Text":
                        case "Note":
                            string textOptions = "<select name='{3}{0}_option'><option value='0'>Entspricht</option><option value='1'>Beginnt mit</option><option value='2'>Enthält</option></select>";
                            if ((Page.Request.Params[ID + filterField.internalName + "_option"] ?? "") != "")
                            {

                                textOptions = textOptions.Replace("'" + Page.Request.Params[ID + filterField.internalName + "_option"] + "'", "'" + Page.Request.Params[ID + filterField.internalName + "_option"] + "' selected");
                            }
                            filter += String.Format("<label for='filter_{0}'>{1}</label><br/><input name='{3}{0}' id='{3}filter_{0}'type='text' class='' value='{2}'/><br/>" + textOptions, filterField.internalName, filterField.name, Page.Request.Params[ID + filterField.internalName] ?? "", ID);
                            break;
                        default:
                            filter += String.Format("<label for='filter_{0}'>{1}</label><br/><input name='{3}{0}' id='{3}filter_{0}'type='text' class='' value='{2}'/><br/>", filterField.internalName, filterField.name, Page.Request.Params[ID + filterField.internalName] ?? "", ID);
                            break;
                    }
                    filter += "<hr/>";
                }
                filter += "<input type='submit' value='Anwenden'/>";
                return filter;
            }
            catch (Exception ex)
            {
                Debug("Filter: " + ex.Message, true);
                return "";
            }
        }

        protected string getOutput()
        {
            try
            {
                if (Config == null)
                {
                    Debug("Bitte die Configuration ausführen.", true);
                    return "";
                }

                string sWeb = "";
                if (String.IsNullOrEmpty(Config.Web.name))
                {
                    sWeb = SPContext.Current.Web.ServerRelativeUrl;
                }
                else
                { 
                    sWeb = Config.Web.url.Replace(SPContext.Current.Site.Url, "");
            }
                if (!sWeb.StartsWith("/"))
                {
                    sWeb = "/" + sWeb;
                }
                using (Web = SPContext.Current.Site.OpenWeb(sWeb))
                {
                    List = Web.Lists[Config.List.name];
                    SPQuery query = new SPQuery();
                    query.Query = this.createCaml();
                    SPListItemCollection Items = List.GetItems(query);
                    string output = "<table class='HDIAggregatedView'><tbody>";
                    Dictionary<string, double> Values = new Dictionary<string, double>();
                    Debug("Affected Items: " + Items.Count.ToString());
                    if (Items.Count > 0)
                    {
                        foreach (SPListItem item in Items)
                        {
                            foreach (var Field in Config.Fields)
                            {
                                Double result = 0;
                                if (Field.aggregate == "Count")
                                {
                                    result = 1;
                                }
                                else
                                {
                                    if (Field.fieldType == "SharePoint")
                                    {
                                        result = getDouble(item, Field.internalName);
                                    }
                                    else if (Field.fieldType == "Formula")
                                    {

                                        List<string> FormulaValues = new List<string>();
                                        foreach (var FormulaField in Field.fields)
                                        {
                                            FormulaValues.Add(getDouble(item, FormulaField.internalName).ToString(CultureInfo.InvariantCulture));
                                        }

                                        result = Helper.calculate(String.Format(Field.formula, FormulaValues.ToArray()));

                                    }
                                }

                                if (Field.aggregate == "Average")
                                {
                                    result /= Items.Count;
                                }
                                if (Values.ContainsKey(Field.internalName))
                                {
                                    Values[Field.internalName] += result;
                                }
                                else
                                {
                                    Values.Add(Field.internalName, result);
                                }
                            }
                        }
                        foreach (var Field in Config.Fields)
                        {
                            try
                            {

                                if (Field.fieldType == "SharePoint")
                                {
                                    if (Field.type == "Calculated")
                                    {
                                        output += String.Format(
    "<tr><td class='field'>{0}</td><td class='values'>{1}</td></tr>",
    String.Format(Field.formatField ?? "{0}", Field.name),
    String.Format(
        Field.format ?? "{0}",
       Values[Field.internalName])
    );
                                    }
                                    else
                                    {
                                        output += String.Format(
                                            "<tr><td class='field'>{0}</td><td class='values'>{1}</td></tr>",
                                            String.Format(Field.formatField ?? "{0}", Field.name),
                                            String.Format(
                                                Field.format ?? "{0}",
                                                List.Fields.GetFieldByInternalName(Field.internalName).GetFieldValueAsHtml(Values[Field.internalName]) ?? "")
                                            );
                                    }
                                }
                                else if (Field.fieldType == "Formula")
                                {
                                    output += String.Format("<tr><td class='field'>{0}</td><td class='values'>{1}</td></tr>", String.Format(Field.formatField ?? "{0}", Field.name), String.Format(Field.format ?? "{0}", Values[Field.internalName]));
                                }
                            }
                            catch
                            {
                                Debug("Das Feld: '" + Field.name + "' scheint ungültige Werte zu haben.", true);
                            }
                        }
                        output += "</tbody></table>";
                        return output;
                    }
                }

                Debug("Die Suchanfrage ergab keine Ergebnisse", true);
                return "";
            }
            catch (Exception ex)
            {
                Debug("Anzeige: " + ex.Message, true);
                return "";
            }
        }

        protected override void CreateChildControls()
        {
            try
            {
                base.CreateChildControls();
                Config = Helper.Deserialize<Config>(sConfig);

                Page.Header.Controls.Add(new LiteralControl(String.Format("<link  href='{0}' rel='stylesheet' type='text/css' />", Config.resourcePath + "css/HDIAggregatedView.css?" + DateTime.Now.ToFileTimeUtc())));
                if (Config.Filter.Count > 0)
                    Controls.Add(new LiteralControl(String.Format("<table class='HDIAggregatedViewLayout'><tr><td class='HDIAggregatedViewFilter'>{0}</td><td>{1}</td></tr></table>", getFilter(), getOutput())));
                else
                    Controls.Add(new LiteralControl(getOutput()));
                if (this._error != "")
                {
                    Controls.Add(new LiteralControl(this._error));
                }
                if (this._debug != "" && Page.Request.Params["Debug"] == "1")
                {
                    Controls.Add(new LiteralControl(this._debug));
                }
                // ScriptManager.RegisterClientScriptResource(this, this.GetType(), "");
                ScriptManager.RegisterClientScriptInclude(this, this.GetType(), "DatePicker", Config.resourcePath + "js/datepicker.js");
                //ReferencePrevalidatedScript("DatePicker", Config.resourcePath+"js/datepicker.js");
                Controls.Add(new LiteralControl(@"
<script type='text/javascript'>
$(function(){
	$('.date').DatePicker({
		format:'d.m.Y',
		date: '" + DateTime.Now.ToString("dd.MM.yyyy") + @"',
		current: '" + DateTime.Now.ToString("dd.MM.yyyy") + @"',
		mode:'range',
		calendars:3,
		position: 'bottom', 
		onChange: function(formated, dates)
		{
			$('.date').val(formated.join(' - '));
			if(dates[0].toDateString() != dates[1].toDateString())
			{  
				$('.date').DatePickerHide();
			}
		}
	});
}); 
</script>"));
            }
            catch (Exception ex)
            {
                Controls.Add(new LiteralControl(String.Format("<div class='error'>{0}</div>", "Allgemein: " + ex.Message)));
            }

        }

        /// <summary>
        /// Reurns the numeric Value behind a ListItem.Field
        /// </summary>
        /// <param name="item">The Listitem where we want the Value from</param>
        /// <param name="field">The internal name of the Feld</param>
        /// <returns>Value as Double</returns>
        private double getDouble(SPListItem item, string field)
        {
            string value = "";
            if (!String.IsNullOrEmpty(field))
            {
                try
                {
                    if (item.Fields.ContainsField(field))
                    {
                        try
                        {
                            value = item[field].ToString();
                        }
                        catch { }
                        switch (item.Fields.GetField(field).Type)
                        {
                            case SPFieldType.Currency:
                            case SPFieldType.Number:
                            case SPFieldType.Integer:
                                if (String.IsNullOrEmpty(value))
                                    return 0;
                                return double.Parse(value);
                            default:
                                if (value.Contains(";#"))
                                {
                                    string[] special = value.Split('#');
                                    if (special.Length > 1)
                                    {
                                        value = special[1];
                                    }
                                    else
                                    {
                                        value = "0";
                                    }

                                    return double.Parse(value, CultureInfo.InvariantCulture);
                                }
                                break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("hmmm..." + ex.ToString());
                }
            }
            throw new Exception(String.Format("Feld: {0} ist keine Zahlenfeld", field));
        }
    }
}
